/*
 * Copyright 2012 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

package com.google.android.apps.mytracks.services.sensors.ant;

/**
 * A counter that processes an Ant+ sensor data (count + event time) and returns
 * the instantaneous cadence value.
 * 
 * @author Chet Henry
 */
public class PowerCounter {
  // The last count
  private int lastCount;
  private int lastRevCount;
  private int lastRpm;
  private int totalRevs;
  private int lastPeriod;
  private int wheelZeros;
  private int lastTorque;
  private double lastMph;
  private double lastWatt;
  private double totalDistance;
  private int torqueAve;

  public PowerCounter() {
    lastCount = -1;
    lastRpm = -1;
    totalRevs = 0;
    wheelZeros = 0;
    lastTorque = 0;
    lastMph = -1;
    lastWatt = -1;
    totalDistance = 0;
  }
  
  /**
   * Sets up the new power value from a standard ant power message.
   * 
   * @param watt
   * @param rpm
   * @param rpm
   */
  public void processMessage(int watt, int rpm) {
    lastRpm = rpm;
    lastWatt = (double) watt;
  }

  /**
   * Sets up the new power value from an ant message with torque.
   * 
   * @param eventCount
   * @param revCount
   * @param rpm
   * @param wheel_period
   * @param torque
   */
  public void processMessageWithTorque(int eventCount, int revCount, int rpm, int wheel_period, int torque) {
    int revDiff = 0;
    int wheel_periodDiff = 0;
    int torqueDiff = 0;
    int eventCountDiff = 0;
    lastRpm = rpm;
    
    if (eventCount < lastCount) {
      // it has rolled over
      eventCountDiff = 256 + eventCount - lastCount;
    } else {
      eventCountDiff = eventCount - lastCount;
    }

    if (revCount < lastRevCount) {
      // it has rolled over
      revDiff = 256 + revCount - lastRevCount;
    } else {
      revDiff = revCount - lastRevCount;
    }
    lastRevCount = revCount;

    totalRevs = revDiff + totalRevs;

    if (wheel_period < lastPeriod) {
      // it has rolled over
      wheel_periodDiff = 65536 + wheel_period - lastPeriod;
    } else {
      wheel_periodDiff = wheel_period - lastPeriod;
    }
    
    if (torque < lastTorque) {
      // it has rolled over
      torqueDiff = 65536 + torque - lastTorque;
    } else {
      torqueDiff = torque - lastTorque;
    }
    lastTorque = torque;
    
    if(eventCountDiff != 0)
      torqueAve = torqueDiff;//(32*eventCountDiff); //Nm
    else
      torqueAve = torqueDiff;
    
    if (wheel_periodDiff > 0 && eventCountDiff > 0) {
      lastPeriod = wheel_period;
      lastCount = eventCount;
      wheelZeros = 0;
    } else {
      if (wheelZeros > 4) {
        lastPeriod = wheel_period;
        lastCount = eventCount;
        lastMph = 0;
        lastWatt = 0;
        return;
      }
      wheelZeros++;
      return;
    }

    double mph = 0;
    double watt = 0;
    if (wheel_periodDiff > 0) {
      //7372.8 = (3600/1000) * 2048
      //2108mm = wheel circumference of a 700c X 25mm tire
      mph = (double) (((7372.8) * 2.108 * (double) eventCountDiff) / ((double) wheel_periodDiff));
      watt = (double) 128 * Math.PI * ((double) torqueDiff / (double) wheel_periodDiff);
      if (watt == 0) watt = 0;
    }

    if (torqueDiff <= 0 && wheel_periodDiff <= 0) {
      mph = 0;
      watt = 0;
    }

    totalDistance = totalRevs * 0.002095;

    if (mph >= 0) {
      lastMph = mph;
    }
    if (watt >= 0) {
      lastWatt = watt;
    }
  }
  
  /**
   * get the current watts as measured
   * @return watts
   */
  public double getPower(){
    return lastWatt;
  }
  
  /**
   * get the current speed
   * @return speed in meters per second
   */
  public double getSpeed(){
    return lastMph;
  }
  
  /**
   * get the current crank rpm
   * @return int rpm
   */
  public int getCadence(){
    return lastRpm;
  }
  
  /**
   * get the current torque in newton meters
   * @return
   */
  public int getTorque(){
    return torqueAve;
  }
  
  /**
   * get the current total distance travelled
   * @return
   */
  public double getTotalDistance(){
    return totalDistance;
  }
}
